import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http'

import { Observable } from 'rxjs/Observable';
/**
 *  谨记这里的observable的‘o’是小写
 */
import { of } from 'rxjs/observable/of';
import { catchError, map, tap } from 'rxjs/operators'

import { Hero } from './hero';
// import { HEROES } from './mock-heroes';

import { MessageService } from './message.service'

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};


@Injectable()
export class HeroService {

  private heroesUrl = 'api/heroes';

  constructor(private httpClient: HttpClient, private messageService: MessageService) { }

  /** GET heroes from the server */
  getHeroes(): Observable<Hero[]>{
    // this.messageService.add('HeroService: fetched heroes')
    return this.httpClient.get<Hero[]>(this.heroesUrl).pipe(
      tap(heroes => this.log(`fetched heroes`)),
      catchError(this.handleError('getHeroes', []))
    )
  }

  /** GET hero by id. Will 404 if id not found */
  getHero(id: number): Observable<Hero> {
    // this.messageService.add(`HeroService: fetched hero id=${id}`);
    const url = `${this.heroesUrl}/${id}`;
    return this.httpClient.get<Hero>(url).pipe(
      tap(_ => this.log(`fetched hero id=${id}`)),
      catchError(this.handleError<Hero>(`getHero id=${id}`))
    )
  }

  /** PUT: update the hero on the server */
  updateHero(hero: Hero): Observable<any> {
    return this.httpClient.put(this.heroesUrl, hero, httpOptions).pipe(
      tap(_ => this.log(`updated hero id=${hero.id}`)),
      catchError(this.handleError<any>(`updateHero`))
    )
  }

  /** POST: add a new hero to the server */
  addHero(hero: Hero): Observable<Hero> {
    return this.httpClient.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
      tap((hero: Hero) => this.log(`add hero w/ id=${hero.id}`)),
      catchError(this.handleError<Hero>(`addHero`))
    )
  }

  /** DELETE: delete the hero from the server */
  deleteHero(hero: Hero | number): Observable<Hero> {
    const id = typeof hero === 'number' ? hero : hero.id;
    const url = `${this.heroesUrl}/${id}`;
    return this.httpClient.delete<Hero>(url, httpOptions).pipe(
      tap(_ => this.log(`delete Hero id=${id}`)),
      catchError(this.handleError<Hero>(`deleteHero`))
    )
  }

  /* GET heroes whose name contains search term */
  searchHeroes(name: string): Observable<Hero[]> {
    if (!name.trim()) {
      return of([])
    }
    return this.httpClient.get<Hero[]>(`api/heroes/?name=${name}`).pipe(
      tap(_ => this.log(`found heroes macthing "${name}"`)),
      catchError(this.handleError<Hero[]>(`searchHeroes`, []))
    )
  }

  /** Log a HeroService message with the MessageService */
  private log(message: string) {
    this.messageService.add('HeroService:' + message)
  }

  /**
   * Handle Http operation that failed.
   * Let the app continue.
   * @param operation - name of the operation that failed
   * @param result - optional value to return as the observable result
   */
  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      this.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }
}
